/*
 * This file defines jval type and
 * all the interfaces on jval.
 */
#ifndef _ZJSON_H_
#define _ZJSON_H_
#ifdef __cplusplus
extern "C" {
#endif

#include "z_list.h"

#if defined(NULL)
#undef NULL
#endif
#define NULL ((void*)0)
#define EXIT_FAILURE 1

typedef int jint;
typedef double jdbl;
typedef char jchar;
typedef char *jstr;
typedef struct list_head *jobj;
typedef struct list_head *jarr;
typedef enum {
    STR, NUM, DBL, OBJ, ARR, BOL, NUL
} vtype;

typedef struct jval {
    union {
        jdbl vdouble;
        jint vint;
        jint vbool;
        jstr vstr;
        jarr varr;
        jobj vobj;
    } data;
    vtype type;
} jval;
typedef struct jpair {
    jstr key;
    jval *val;
    struct list_head list;
} jpair;

typedef struct jelem {
    jval *val;
    struct list_head list;
} jelem;

/* These declarations depend on stdio.h */
extern int printf(const char *, ...);

/* These declarations depend on stdlib.h */
extern void *malloc(size_t);

extern void free(void *);

extern void exit(int);

extern int atoi(const char *s);

extern double atof(const char *s);

/* These declarations depend on string.h or strings.h*/
extern void *memcpy(void *, const void *, size_t);

extern void *memset(void *, int, size_t);

extern size_t strlen(const char *);

extern int strcmp(const char *, const char *);

extern int strncmp(const char *, const char *, size_t);

/* This declaration depends on assert.h */
#ifndef assert
#define assert(cond)  do{ \
                            if (cond); \
                            else { \
                                printf("[%s] %s():%d\n\tAssert ("#cond ") failed!\n", \
                                    __FILE__, __FUNCTION__,__LINE__); \
                                exit(1); \
                            } \
                        }while(0)
#endif
/* Build functions */

char *zJSON_strdup(const char *s);
/* @brief Build a JSON node */
jval *zJSON_new_jval(vtype type);

jval *zJSON_new_int(jint n);
jval *zJSON_new_double(jdbl n);
jval *zJSON_new_string(jstr s);
jval *zJSON_new_true();
jval *zJSON_new_false();
jval *zJSON_new_null();
/* @brief Build a JSON object pair. If key points to an allocated c string */
jpair *zJSON_new_jpair(jchar *key, jval *val);
jpair *zJSON_new_jpair_CS(jchar *key, jval *val);

/* @brief Build a JSON array item */
jelem *zJSON_new_jelem(jval *val);

/* Destroy functions */
void zJSON_free_jstr(jstr str);
void zJSON_free_jval(jval *node);
void zJSON_free_jpair(jpair *pair);
void zJSON_free_jpair_CS(jpair *pair);
void zJSON_free_jelem(jelem *elem);

/* @brief set JSON node type */
void zJSON_set_type(jval *node, vtype type);

/* @brief Append a key (NOT constant) to a JSON object */
void zJSON_set_key(jval *node, jchar *key);

/* @brief Set key's value */
void zJSON_set_key_value(jval *node, const jchar *key, jval *val);

/* @brief Append a jval to a JSON array */
void zJSON_set_arr_value(jval *node, jval *val);

/* @brief Whether a JSON object has key */
int zJSON_has_key(jval *node, const jchar *key);

jstr zJSON_get_str(jval *node);

jint zJSON_get_int(jval *node);

jdbl zJSON_get_dbl(jval *node);

jint zJSON_get_bol(jval *node);

jarr zJSON_get_arr(jval *node);

jobj zJSON_get_obj(jval *node);

size_t zJSON_get_arr_length(jval *arr);

size_t zJSON_get_obj_length(jval *obj);

jval *zJSON_get_arr_item(jval *arr, int idx);

jval *zJSON_get_key_val(jval *obj, const jchar *key);

/* Note:
 * - If the key is a constant,
 *     calling the function WITH a suffix "_CS"
 *     would raise a segment fault.
 * - Otherwise if the key is an allocated c-string,
 *     calling the function WITHOUT a suffix "_CS"
 *     would make a memory leak.
 * */
void zJSON_del_key(jval *obj, char *key);

void zJSON_del_key_CS(jval *obj, const char *key);

/* Note:
 * - If you are maintaining a jval * ptr point to
 *     the item which would be deleted/replaced, the ptr
 *     would be discarded since the ptr would be free()
 *     in this function.
 * - If you wanna go on maintaining it, you should
 *     dump it by zJSON_dump()
 */
void zJSON_del_arr_item(jval *arr, unsigned int idx);

void zJSON_rpl_arr_item(jval *arr, unsigned int idx, jval *newValue);

void zJSON_rpl_key_val(jval *obj, const char *key, jval *newValue);

#define zJSON_arr_for_each_elem(arr, itr) \
    list_foreach_entry_reverse(arr->data.varr,jelem,list,itr)
#define zJSON_obj_for_each_pair(arr, itr) \
    list_foreach_entry_reverse(arr->data.vobj,jpair,list,itr)
#endif

/* @brief Parse JSON string to jval structure */
jval *zJSON_parse(const jchar *json);

/* @brief Execute a JSONPath query */
jval *zJSON_exec(jval *root, const char *path);

/* @brief render JSON node to string with some options */
void zJSON_render(jval *root,
                   const int prettify_flag, size_t indent, size_t indentStep,
                   int color_flag,
                   int braces_flag);

/* @brief Minify a JSON node and print to stdout */
void zJSON_minify(jval *root);

/* @brief Minify a JSON node and print to buffer */
void zJSON_minify_to(jval *root, jchar *buffer, size_t len);

/* @brief Prettify a JSON node and print to stdout */
void zJSON_prettify(jval *root, size_t indent);

/* @brief Prettify a JSON node and print to buffer */
void zJSON_prettify_to(jval *root, size_t indent, jchar *buffer, size_t len);

/* @brief Prettify a JSON node and print to stdout with colors (BASH compatible)*/
void zJSON_colorfy(jval *root);

#ifdef __cplusplus
}
#endif
